#include "net/TcpServer.h"
#include "net/TcpConnection.h"
#include "net/EventLoop.h"
#include "net/EventLoopThreadPool.h"
#include "Base/Logger.h"
namespace toym
{
namespace net
{

//////////////////////////////////////////////////
//////  Static Members
//////////////////////////////////////////////////
std::atomic_int64_t TcpServer::s_connSeq(0);


///////////////////////////////////////////////////
///////   Public Methods
///////////////////////////////////////////////////
TcpServer::TcpServer(EventLoop* loop, const InetAddress& addr, const std::string& name)
    : m_loop(loop),
      m_serverName(name),
      m_acceptor(loop, addr),
      m_connectionCb(TcpServer::defaultConnectionCb),
      m_messageCb(TcpServer::defaultMessageCb),
      m_conns(),
      m_threadPool(new EventLoopThreadPool(loop))
{
    m_acceptor.setNewConnectionCallback(std::bind(
        &TcpServer::handleNewConnection, 
        this, 
        std::placeholders::_1, 
        std::placeholders::_2)
        );
    
}

TcpServer::~TcpServer() = default;

void TcpServer::setThreadNum(int nThread) {
    m_threadPool->setThreadNum(nThread);
}

void TcpServer::start() {
    m_acceptor.listen();
    m_threadPool->start();
}

void TcpServer::removeConnection(const TcpConnectionPtr& conn) {
    if (m_loop->isInLoopThread()) {
        removeConnectionInThread(conn);
    }else {
        m_loop->runInLoop(std::bind(&TcpServer::removeConnectionInThread, this, conn));
    }
}


///////////////////////////////////////////////////
///////   Private Methods
///////////////////////////////////////////////////
void TcpServer::removeConnectionInThread(const TcpConnectionPtr& conn) {
    m_loop->assertInLoopThread();
    LOG_INFO << "TcpServer::removeConnection [" 
             << m_serverName 
             << "], connection "
             << conn->getConnName();
    size_t n = m_conns.erase(conn->getConnName());
    assert(n == 1);
    conn->getLoop()->enqueueLoop(std::bind(&TcpConnection::connectionDestroy, conn));
}

void TcpServer::handleNewConnection(Socket sock, const InetAddress& peerAddr) {
    char connName[32];
    InetAddress localAddr = sock.getLocalAddr();
    ++s_connSeq;
    std::snprintf(connName, sizeof(connName), "Connection#%ld", s_connSeq.load());
    std::string name(connName);
    LOG_INFO << "[" << m_serverName <<"] new Connection " << connName;
    EventLoop* nextLoop = m_threadPool->nextLoop();
    std::shared_ptr<TcpConnection> pConn(new TcpConnection(std::move(sock), nextLoop, localAddr, peerAddr));
    pConn->setConnName(name);
    pConn->setCloseCb(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
    pConn->setConnectionCb(m_connectionCb);
    pConn->setMessageCb(m_messageCb);
    m_conns[name] = pConn;
    // 要在新的循环里面运行
    nextLoop->runInLoop(std::bind(&TcpConnection::connectionEstablished, pConn));
}

void TcpServer::defaultConnectionCb(const TcpConnectionPtr& conn) {
    if(conn->connected()) {
        LOG_INFO << "new connectin from " << conn->getLocalAddr().toIpPort();
    }
    else {
        LOG_INFO << "connection from " << conn->getLocalAddr().toIpPort() << " disconnected";
    }
}
void TcpServer::defaultMessageCb(const TcpConnectionPtr& conn, Buffer* buf, Timestamp ts) {
    LOG_INFO << buf->size() << " bytes from " << conn->getPeerAddr().toIpPort();
    buf->readAll();
}

} // namespace net

} // namespace toym

